﻿using TTUnityPlugin;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TTGameEngine;

namespace TTGame {

public delegate void Func_LoadEffectOK(AudioClip clip);

/// <summary>
/// AudioType会反映在声音的名字前缀上。相同前缀将会存放在同一个目录下。注：不同的前缀也可能是同一个目录。
/// </summary>
public enum AudioType {
    BGM,
    Voice,
    Players,
    Enemies,
    Partners,
    Guns,
    Bullets,
    Boom,
    UI,
    Count,
}

class BGMAudioTable {
    string bgmName;
    string[] bgmAudioList;

    public BGMAudioTable(string aBGMName, int num) {
        bgmName = aBGMName;
        bgmAudioList = new string[num];
    }


}


/// <summary>
/// 背景音乐播放
///
/// 为了减少音乐播放时的大小。将一整个音乐分成一段段最多1min的片段，挨个播放，及时释放内存。LTGr
///
/// </summary>
public class BGMTableMgr {
    string curBgmName;
    int curBgmPartIdx = -1;
    BGMAudioTable curBGMAudioList;
    Dictionary<string, BGMAudioTable> bgmTables = new Dictionary<string, BGMAudioTable>();
    Dictionary<string, int> bgmPartsTables = new Dictionary<string, int>();
    static BGMTableMgr instance;

    public static BGMTableMgr Ins {
        get {
            if(instance == null) {
                instance = new BGMTableMgr();
            }
            return instance;
        }
    }

    BGMTableMgr() {

    }

    public string GetBGMAudioPartName(string name, int idx) {
        if(string.IsNullOrEmpty(name)) {
            return "";
        }
        return name + idx;
    }

    public void loadBGMTable(string name) {
        BGMAudioTable table = null;
        if(bgmTables.TryGetValue(name, out table)) {
            table = new BGMAudioTable(name, 1);

        }
    }
}

/// <summary>
/// 播放音效或者背景音乐，管理声音源的缓存池。
///
/// mark:youhua 最好改个名字，不叫AudioManager。在Preofiler看到一个AudiManager，似乎是系统内部的模块名。
///
/// You Should Know
/// 1、AudioClip应该提前加载。
/// 2、直接使用name播放声音。对于循环播放的声音，使用name取得AudioSource后，自行决定何时停止。
/// 3、编辑器自己添加的声音源的AudioClip不可以编辑器中设置。代码中预先加载并赋值。（尽量不要自己添加声音源。）
///
/// Intro.
/// 1、仅提供基本功能，不提供方法参数的默认值。更多细节与扩展，由另外的扩展类处理，如：AudioSourceExtensions。
/// （扩展指的是不需要额外的管理变量成员，仅通过参数改变来增加功能。）
/// 2、AudioListener和AudioSource的处理。
///    AudioListener由AudioManager创建于其之下，坐标由场景Camera实时更新。
///    声音源的坐标等于产生者对象的坐标。UI与背景音乐的声音源为AudioListener坐标。
///    非循环音效的坐标，在创建时设置之后不再改变。循环音效会将声音源放于发生者之下，坐标随之改变。
/// </summary>
public class AudioManager : MonoBehaviour {
    #region Fields
    static AudioManager instance = null;
    public static string PrefabPath = "Prefabs/Logic/AUDIO MANAGER";
    const float BGMusicFadeDuration = 2f;
    public string[] clipsGameplayNames;
    public string clipMenus;

    //背景音乐（有淡入淡出效果）
    [SerializeField]
    AudioSource bgmSource;
    [SerializeField]
    AudioSource emergingAudioSource;
    [SerializeField]
    Transform EffectsPool;
    //语音
    public AudioSource voiceSource;

    private bool voiceSource2LoadingRes = false; //声音源2是否在加载资源
    public AudioSource voiceSource2;

    public static readonly float WeakVolume = 0.1f; //被削弱的声音音量大小

    PoolCacher<AudioSource> AudioSourcePool;
    //用户自己创建自己管理的声音源，只是在这里注册一下以便统一开关
    [HideInInspector]
    public List<AudioSource> userAudioSources = new List<AudioSource>();
    //延时播放的音效
    Queue<AudioInfo> queuedClips = new Queue<AudioInfo>();
    Dictionary<AudioSource, float> pausingSources = new Dictionary<AudioSource, float>();
    //音效加载
    [HideInInspector]
    public AudioClipLoader AudioLoader;
    Dictionary<string, bool> pauseIgnoreAudios = new Dictionary<string, bool>();
    //-----用户自行控制的声音源。-----
    static CountID_UInt AudioGlobalID = new CountID_UInt();
    Dictionary<uint, string> m_loadingAuido = new Dictionary<uint, string>();
    Dictionary<uint, AudioSource> m_controlAudio = new Dictionary<uint, AudioSource>();

    //音量0~1
    float effectVolume = 1f;
    float musicVolume = 1f;
    bool m_enableSounds = true;
    bool m_enableBGM = true;
    bool isBGMPause = false; //只有isPause才算暂停。BGM停止时，isBGMPause = false。
    //游戏暂停。对BGM无影响，所有音效不再播放。会有个别UI音效例外。
    bool isAudioPausing = false;
    string curBGMusicName = "";
    [HideInInspector]
    public Transform audioListener;
    bool playingQueuedClips = false;
    AudioClip fadeToClip;
    float fadeDuration;
    public static float BGMVolumeFactor = 1f;
    public static string AudioMgrPrefab = "default/PVE/Prefabs/Game/AUDIO MANAGER";

    #endregion Fields

    #region Properties

    public static AudioManager Ins {
        get {
            if(instance == null) {
                //instance = FindObjectOfType(typeof(AudioManager)) as AudioManager;
                //if(instance == null) {
                instance = ((GameObject)Instantiate(GameObjectLoader.Ins.GetPrefabObj(AudioMgrPrefab))).GetComponent<AudioManager>();
                //DontDestroyOnLoad(instance);
                //}
                instance.init();
            }
            return instance;
        }
    }
    void Awake() {
        instance = this;
    }

    void Start() {
        //if(DebugSetting.Self.Sound_IsUseCoroutine == false) {
        StartCoroutine(C_TimeToClearStoppedAudio());
        //}
    }

    public void init() {
        AudioSourcePool = new PoolCacher<AudioSource>(CreateAudioSourceForPool);
        AudioSourcePool.Capacity = 10;
        AudioLoader = gameObject.AddComponentSafe<AudioClipLoader>();
        //AOK兼容，AudioListener使用SoundController的。
        //audioListener = SoundController.getInstance().transform;
        audioListener = gameObject.AddComponentSafe<AudioListener>().transform;
        EffectVolume = 1;
        MusicVolume = 1;

        //暂停时不停止UI按键声
        pauseIgnoreAudios.Add("ui_dianji5", true);

        InitBGMSetting();
    }

    public float EffectVolume {
        get {
            return effectVolume;
        } set {
            effectVolume = value;
            var list = AudioSourcePool.getUsingList();
            for(int i = 0; i < list.Count; i++) {
                var audioSource = list[i];
                if(audioSource) {
                    audioSource.volume = value;
                }
            }
            voiceSource.volume = value;
            voiceSource2.volume = value;
        }
    }

    public float MusicVolume {
        set {
            musicVolume = value * BGMVolumeFactor;
            if(bgmSource) {
                bgmSource.volume = musicVolume;
            }
        }
        get {
            return musicVolume;
        }
    }

    public Vector3 AudioListenerPos {
        get {
            if(audioListener == null) {
                return audioListener.position;
            } else {
                return transform.position;
            }
        }
    }
    #endregion Properties
    public string[] bgmSetting;
    protected Dictionary<string, byte> bgmDataDic = new Dictionary<string, byte>();
    protected string bgmNowName = string.Empty;
    protected int bgmNameIndex = 0;
    protected bool bgmListening = false;
    protected bool bgmLoopSet = false;
    #region Play BGM
    int curBGMPartIdx = 1;

    public void PlayBGM(string bgm) {
        playBGM(bgm, 0);
        if(!bgmListening) {
            StartCoroutine(BGMPlayListen());
            bgmListening = true;
        }
    }

    void playBGM(string bgmName, int partIdx) {
        curBGMusicName = bgmName;
        if(m_enableBGM == false) {
            return;
        }
        if(bgmSource == null) {
            return;
        }

        if(string.IsNullOrEmpty(bgmName)) {
            StopBGM();
            return;
        }
        int count = 0;
        if(bgmDataDic.ContainsKey(curBGMusicName)) {
            count = bgmDataDic[curBGMusicName];
        }
        if(partIdx == 0) {
            partIdx = UnityEngine.Random.Range(0, count);
        }
        if(count <= 1) {
            bgmNameIndex = 1;
            bgmLoopSet = true;
        } else {
            bgmNameIndex = partIdx % count + 1;
            bgmLoopSet = false;
        }
        bgmNowName = bgmName + "_" + bgmNameIndex.ToString();

        //curBGMPartIdx = partIdx;
        //string bgmPartName = BGMTableMgr.Ins.GetBGMAudioPartName(bgmName, partIdx);
        //if(string.IsNullOrEmpty(bgmPartName)) {
        //    return;
        //}
        AudioLoader.LoadClip(bgmNowName, (path, obj) => {
            AudioClip clip = obj as AudioClip;
            if(bgmSource.isPlaying) {
                //fade切换
                CrossFadeMusicTo(clip, BGMusicFadeDuration);
            } else {
                //直接播放
                bgmSource.transform.position = AudioListenerPos;
                bgmSource.clip = clip;
                bgmSource.loop = bgmLoopSet;
                bgmSource.volume = MusicVolume;
                isBGMPause = false;
                bgmSource.Play();
            }
        });
    }

    void InitBGMSetting() {
        char[] splitVar = { ',' };
        string[] info = null;
        if(bgmSetting != null && bgmSetting.Length > 0) {
            for(int i = 0; i < bgmSetting.Length; i++) {
                if(!string.IsNullOrEmpty(bgmSetting[i])) {
                    info = bgmSetting[i].Split(splitVar, System.StringSplitOptions.RemoveEmptyEntries);
                    if(info.Length == 2) {
                        bgmDataDic.Add(info[0], byte.Parse(info[1]));
                    }
                }
            }
        }
    }

    IEnumerator BGMPlayListen() {
        const float listenDelay = 0.5f;

        while(bgmSource) {
            while(!bgmSource.clip || bgmSource.clip.name != bgmNowName || !bgmSource.isPlaying) {
                yield return new WaitForSeconds(listenDelay);
            }

            while(bgmSource.isPlaying) {
                if((voiceSource && voiceSource.isPlaying) || (voiceSource2 && voiceSource2.isPlaying)) {
                    bgmSource.volume = WeakVolume;
                } else {
                    bgmSource.volume = MusicVolume;
                }

                yield return new WaitForSeconds(listenDelay);
            }

            if(isBGMPause == false && bgmSource.clip && bgmSource.clip.name == bgmNowName) {
                playBGM(curBGMusicName, bgmNameIndex);
            }
            yield return new WaitForSeconds(listenDelay);
        }

        bgmListening = false;
    }

    void playNextPartBGM() {
        int idx = curBGMPartIdx + 1;
        if(idx > 10) {
            idx = 0;
        }
        playBGM(curBGMusicName, idx);
    }

    IEnumerator BGMStop(string bgmName, int idx, float time) {
        yield return new WaitForSeconds(time);
        if(bgmName.Equals(curBGMusicName) == false || curBGMPartIdx != idx) {
            DebugUtil.LogErrorFormat("[AudioMgr] 背景音乐的检测不匹配。 curBGM[{0}, {1}] detection[{2}, {3}] ", curBGMusicName, curBGMPartIdx, bgmName, idx);
        }
        playNextPartBGM();
    }

    public void RestartBGM() {
        if(m_enableBGM == false) {
            return;
        }
        if(bgmSource == null) {
            return;
        }

        if(bgmSource.clip == null) {
            PlayBGM(curBGMusicName);
        } else {
            isBGMPause = false;
            bgmSource.Play();
        }
    }

    public void StopBGM() {
        if(bgmSource == null) {
            return;
        }

        isBGMPause = false;
        bgmSource.Stop();
        if(bgmSource.clip) {
            string clipname = bgmSource.clip.name;
            bgmSource.clip = null;
            AudioLoader.ClearAudioMemory(clipname);
        }

    }
    //从中断处继续播放。如果当前已停止，则重新播放。
    public void ResumeBGM() {
        if(m_enableBGM == false) {
            return;
        }
        if(bgmSource == null) {
            return;
        }

        isBGMPause = false;
        if(bgmSource.isPlaying) {
            bgmSource.UnPause();
        } else {
            if(bgmSource.clip == null) {
                PlayBGM(curBGMusicName);
            } else {
                bgmSource.Play();
            }
        }
    }
    //暂停。（可从暂停处恢复播放）
    public void PauseBGM() {
        if(bgmSource == null) {
            return;
        }
        isBGMPause = true;
        bgmSource.Pause();
    }

    #endregion BGM Methods

    #region Play Effect
    public void PreloadAudioClip(string name) {
        AudioLoader.LoadClip(name, null);
    }

    public void EnableSounds(bool b) {
        m_enableSounds = b;
        if(b == false) {
            foreach(AudioSource aSource in AudioSourcePool.getUsingList()) {
                if(aSource)
                    aSource.Stop();
            }
            foreach(AudioSource aSource in userAudioSources) {
                if(aSource)
                    aSource.Stop();
            }
            if(voiceSource)
                voiceSource.Stop();
            if(voiceSource2)
                voiceSource2.Stop();
        }
    }

    public void EnableBGM(bool b) {
        m_enableBGM = b;
        if(b) {
            ResumeBGM();
        } else {
            PauseBGM();
        }
    }

    public void PauseAllSounds() {
        isAudioPausing = true;
        foreach(AudioSource aSource in AudioSourcePool.getUsingList()) {
            PauseSource(aSource);
        }
        foreach(AudioSource aSource in userAudioSources) {
            PauseSource(aSource);
        }

        PauseSource(voiceSource);
        PauseSource(voiceSource2);
    }

    public void ResumeAllSound() {
        isAudioPausing = false;
        foreach(KeyValuePair<AudioSource, float> pair in pausingSources) {
            AudioSource audioS = pair.Key;
            float pauseTime = pair.Value;
            if(audioS) {
                if(audioS.clip.length > pauseTime) {
                    audioS.time = pauseTime;
                    audioS.UnPause();
                } else {
                    DebugUtil.LogFormat("resume Sound. over time:[{0}] recordtime:[{1}] len[{2}]", audioS.name, audioS, audioS.clip.length);
                }
            }
        }
        pausingSources.Clear();
    }

    public void PlayDelayEffect(AudioInfo ai) {
        queuedClips.Enqueue(ai);
        if(!playingQueuedClips) {
            StartCoroutine(C_PlayQueuedEffects());
        }
    }

    //播放音效（坐标）。异步。
    public void PlayEffect(string name, Vector3 pos, float aVolume, float aPitch, float aMinDistance, bool fromUSS = false) {
        if(m_enableSounds == false) {
            return;
        }
        if(string.IsNullOrEmpty(name) || !isCanPlayWhilePausing(name)) {
            return;
        }

        AudioLoader.LoadClip(name, (path, clip) => {
            if(clip == null) {
                return;
            }
            PlayClip(clip as AudioClip, pos, aVolume, aPitch, aMinDistance, false, fromUSS);
        });
    }
    //播放音效（对象）。异步
    public void PlayEffect(string name, Transform parent, float aVolume, float aPitch, float aMinDistance, bool fromUSS = false) {
        if(m_enableSounds == false) {
            return;
        }
        if(string.IsNullOrEmpty(name) || !isCanPlayWhilePausing(name)) {
            return;
        }

        AudioLoader.LoadClip(name, (path, clip) => {
            if(clip == null) {
                return;
            }
            PlayClip(clip as AudioClip, parent, aVolume, aPitch, aMinDistance, false, fromUSS);
        });
    }

    /// <summary>
    /// playTime必须填。如果自行控制结束，应该调用PlayerEffectCustom。
    /// </summary>
    /// <param name="name"></param>
    /// <param name="parent"></param>
    /// <param name="playTime"></param>
    /// <param name="aVolume"></param>
    /// <param name="aPitch"></param>
    /// <param name="aMinDistance"></param>
    /// <param name="fromUSS"></param>
    public uint PlayLoopAudioWithTime(string name, Transform parent, float playTime, float aVolume, float aPitch, float aMinDistance, bool fromUSS = false) {
        if(m_enableSounds == false) {
            return CountID_UInt.UNINIT_VALUE;
        }
        if(string.IsNullOrEmpty(name) || !isCanPlayWhilePausing(name) || playTime <= 0) {
            return CountID_UInt.UNINIT_VALUE;
        }

        uint AudioKey = AudioGlobalID.GetGID();
        m_loadingAuido.Add(AudioKey, name);
        AudioLoader.LoadClip(name, (path, clip) => {
            if(clip == null) {
                return;
            }
            AudioSource audioS = PlayClip(clip as AudioClip, parent, aVolume, aPitch, aMinDistance, true, fromUSS);
            RegistAudioLoopUpdate(AudioKey, audioS, playTime);
            m_controlAudio.Add(AudioKey, audioS);
        });
        return AudioKey;
    }

    public uint PlayLoopAudioWithTime(string name, Vector3 pos, float playTime, float aVolume, float aPitch, float aMinDistance, bool fromUSS = false) {
        if(m_enableSounds == false) {
            return CountID_UInt.UNINIT_VALUE;
        }
        if(string.IsNullOrEmpty(name) || !isCanPlayWhilePausing(name) || playTime <= 0) {
            return CountID_UInt.UNINIT_VALUE;
        }

        uint AudioKey = AudioGlobalID.GetGID();
        m_loadingAuido.Add(AudioKey, name);
        AudioLoader.LoadClip(name, (path, clip) => {
            if(clip == null) {
                return;
            }
            AudioSource audioS = PlayClip(clip as AudioClip, pos, aVolume, aPitch, aMinDistance, true, fromUSS);
            RegistAudioLoopUpdate(AudioKey, audioS, playTime);
            m_controlAudio.Add(AudioKey, audioS);
        });
        return AudioKey;
    }

    //用户自行控制声音源的音效（坐标）。audioClip需要自行确保已加载。
    //m_enableSounds 为 false的话，仍然会返回接口，但是在PlayClip里面产生的AudioSource会处于stop状态。
    //静音时，将返回null
    public uint PlayerEffectCustom(string name, Vector3 pos, float aVolume, float aPitch, float aMinDistance, bool aLoop, bool fromUSS = false) {
        if(m_enableSounds == false) {
            return CountID_UInt.UNINIT_VALUE;
        }
        if(string.IsNullOrEmpty(name) || !isCanPlayWhilePausing(name)) {
            return CountID_UInt.UNINIT_VALUE;
        }

        uint AudioKey = AudioGlobalID.GetGID();
        m_loadingAuido.Add(AudioKey, name);
        AudioLoader.LoadClip(name, (path, clip) => {
            if(clip == null && m_loadingAuido.Remove(AudioKey)) {
                return;
            }
            AudioSource audioSource = PlayClip(clip as AudioClip, pos, aVolume, aPitch, aMinDistance, aLoop, fromUSS);
            m_controlAudio.Add(AudioKey, audioSource);
        });
        return AudioKey;
    }

    //用户自行控制声音源的音效（对象）。audioClip需要自行确保已加载。
    public uint PlayerEffectCustom(string name, Transform parent, float aVolume, float aPitch, float aMinDistance, bool aLoop, bool fromUSS = false) {
        if(m_enableSounds == false) {
            return CountID_UInt.UNINIT_VALUE;
        }
        if(string.IsNullOrEmpty(name) || !isCanPlayWhilePausing(name)) {
            return CountID_UInt.UNINIT_VALUE;
        }

        uint AudioKey = AudioGlobalID.GetGID();
        m_loadingAuido.Add(AudioKey, name);
        AudioLoader.LoadClip(name, (path, clip) => {
            if(clip == null && m_loadingAuido.Remove(AudioKey)) {
                return;
            }
            AudioSource audioSource = PlayClip(clip as AudioClip, parent, aVolume, aPitch, aMinDistance, aLoop, fromUSS);
            m_controlAudio.Add(AudioKey, audioSource);
        });
        return AudioKey;
    }

    //需要与PlayerEffectLoop配套使用。
    public void StopEffectCustom(uint audioKey) {
        if(m_loadingAuido.Remove(audioKey)) {
            return;
        }

        AudioSource audioS = null;
        m_controlAudio.TryGetValue(audioKey, out audioS);
        loopAudioTimeMonitor.Remove(audioKey);

        if(audioS != null) {
            m_controlAudio.Remove(audioKey);
            //audioS.gameObject.SetActive(false);
            audioS.Stop();
            audioS.transform.parent = EffectsPool;
            audioS.clip = null;
            AudioSourcePool.BackToCache(audioS);
        }
    }

    AudioSource PlayClip(AudioClip aClip, Vector3 aPos, float aVolume, float aPitch, float aMinDistance, bool aLoop, bool fromUSS = false) {
        AudioSource free = PlayClip(aClip, aVolume, aPitch, aMinDistance, aLoop);
        if(free == null) {
            return null;
        }
        free.transform.position = aPos;

        return free;
    }

    AudioSource PlayClip(AudioClip aClip, Transform aParent, float aVolume, float aPitch, float aMinDistance, bool aLoop, bool fromUSS = false) {
        AudioSource free = PlayClip(aClip, aVolume, aPitch, aMinDistance, aLoop);
        if(free == null) {
            return null;
        }
        free.transform.parent = aParent;
        free.transform.localPosition = Vector3.zero;
        return free;
    }


    #endregion Play Effect

    public void PlayNPCVoice(string name, bool isImportant = false) {
        if(m_enableSounds == false) {
            return;
        }
        AudioLoader.LoadClip(name, (path, clip) => {
            if(!voiceSource.isPlaying || isImportant) {
                voiceSource.transform.position = AudioListenerPos;
                voiceSource.time = 0;
                voiceSource.Stop();
                voiceSource.clip = (clip as AudioClip);
                voiceSource.Play();
                //if(bgmSource && bgmSource.volume > 0.2f) {
                //    bgmSource.volume = 0.2f;
                //}
                SetAllAudioEffectVolume(WeakVolume);
            }
        });
    }

    //播放 多人战斗击杀音效
    public void PlayKillTips(string name, bool isImportant = false) {
        if(m_enableSounds == false) {
            return;
        }
        voiceSource2LoadingRes = true;
        AudioLoader.LoadClip(name, (path, clip) => {
            voiceSource2LoadingRes = false;
            if(!voiceSource2.isPlaying || isImportant) {
                voiceSource2.transform.position = AudioListenerPos;
                voiceSource2.time = 0;
                voiceSource2.Stop();
                voiceSource2.clip = (clip as AudioClip);
                voiceSource2.Play();
                //if(bgmSource && bgmSource.volume > 0.2f) {
                //    bgmSource.volume = 0.2f;
                //}
                SetAllAudioEffectVolume(WeakVolume);
            }
        });
    }

    public bool IsPlayingKillTipVoice() {
        return voiceSource2LoadingRes || voiceSource2 && voiceSource2.isPlaying;
        //if (voiceSource2LoadingRes) return true;
    }


    private void SetAllAudioEffectVolume(float volume) {
        foreach(AudioSource aSource in AudioSourcePool.getUsingList()) {
            if(aSource && aSource.isPlaying) {
                aSource.volume = volume;
            }
        }
        foreach(AudioSource aSource in userAudioSources) {
            if(aSource && aSource.isPlaying) {
                aSource.volume = volume;
            }
        }
    }
    #region Private Methods

    void OnDestroy() {
        if(instance == this)
            instance = null;
    }

    void PauseSource(AudioSource aSource) {
        if(aSource && aSource.isPlaying && !pausingSources.ContainsKey(aSource) && !pauseIgnoreAudios.ContainsKey(aSource.clip.name)) {
            DebugUtil.LogFormat("Pause Sound time:[{0}]", aSource.time);
            pausingSources.Add(aSource, aSource.time);
            aSource.Pause();
        }
    }

    /// <summary>
    /// 背景音乐渐隐渐现的切换效果。
    /// </summary>
    /// <param name="aNewMusicClip"></param>
    /// <param name="aNewChangeDuration"></param>
    void CrossFadeMusicTo(AudioClip aNewMusicClip, float aNewFadeDuration) {
        if(aNewMusicClip != bgmSource.clip) {
            isBGMPause = false;
            StopCoroutine("Crossfade");
            fadeToClip = aNewMusicClip;
            fadeDuration = aNewFadeDuration;
            StartCoroutine("Crossfade");
        }
    }

    IEnumerator Crossfade() {
        isBGMPause = false;
        AudioSource fadingAudioSource = bgmSource;
        emergingAudioSource.time = 0;
        emergingAudioSource.volume = 0;
        emergingAudioSource.loop = bgmLoopSet;
        emergingAudioSource.clip = fadeToClip;
        emergingAudioSource.transform.position = AudioListenerPos;
        emergingAudioSource.Play();
        float startTime = Time.time;
        float endTime = startTime + fadeDuration;

        while(Time.time < endTime) {
            float i = (Time.time - startTime) / fadeDuration;

            fadingAudioSource.volume = Mathf.Lerp(MusicVolume, 0, i);
            emergingAudioSource.volume = Mathf.Lerp(0, MusicVolume, i);

            yield return null;

        }
        emergingAudioSource.volume = MusicVolume;
        fadingAudioSource.Stop();
        fadingAudioSource.volume = 0;
        bgmSource = emergingAudioSource;
        emergingAudioSource = fadingAudioSource;

        AudioLoader.ClearAudioMemory(emergingAudioSource.clip.name);
        emergingAudioSource.clip = null;
    }

    IEnumerator C_PlayQueuedEffects() {
        playingQueuedClips = true;
        while(queuedClips.Count > 0) {
            AudioInfo ai = queuedClips.Dequeue();
            if(ai.delay >= 0) {
                yield return new WaitForSeconds(ai.delay);
            }

            if(ai.parent) {
                PlayEffect(ai.name, ai.parent, ai.volume, ai.pitch, ai.minDistance);
            } else {
                PlayEffect(ai.name, ai.position, ai.volume, ai.pitch, ai.minDistance);
            }
        }
        playingQueuedClips = false;
    }

    bool isCanPlayWhilePausing(string audioSourceName) {
        return !isAudioPausing || pauseIgnoreAudios.ContainsKey(audioSourceName);
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="aClip"></param>
    /// <param name="aVolume"></param>
    /// <param name="aPitch">播放速度</param>
    /// <param name="aMinDistance">在最小距离之内音量最大，之外开始衰减</param>
    /// <param name="loop"></param>
    /// <returns></returns>
    AudioSource PlayClip(AudioClip aClip, float aVolume, float aPitch, float aMinDistance, bool loop) {
        string sourceName = "AudioSource : " + aClip.name;
        //free，空闲的。初始父节点是“Effects Pool”
        AudioSource free = AudioSourcePool.ObtainObj();
        if(free == null) {
            return null;
        }
        free.loop = loop;
        free.transform.position = Vector3.zero;

        if((voiceSource && voiceSource.isPlaying) || (voiceSource2 && voiceSource2.isPlaying)) {
            free.volume = WeakVolume;
        } else {
            free.volume = EffectVolume * aVolume;
        }

        //start the sound and also start the coroutine that will deactivate it after playing the sound
        free.minDistance = aMinDistance;
        free.clip = aClip;
        free.name = sourceName;
        free.pitch = aPitch;
        free.time = 0;
        free.spatialBlend = 0.0f;

        if(m_enableSounds) {
            //free.gameObject.SetActive(true);
            free.Play();
        } else {
            DebugUtil.LogErrorFormat("[AudioManager] 声音已关闭，但是仍旧创建了声音源");
            //free.Stop();
        }


        if(loop == false) {
            //播完自动删.
            RegistAudioGC(free);
        }

        return free;
    }

    float TimeToClear_FastTime = 0.5f;
    float TimeToClear_SlowTime = 3f;
    float TimeToClear_interval = 1f;
    //所有正在播放的AudioSource
    LinkedList<AudioSource> playingAudioClips = new LinkedList<AudioSource>();
    //限定了播放时间的循环音效
    Dictionary<uint, AudioSourceInfo> loopAudioTimeMonitor = new Dictionary<uint, AudioSourceInfo>();
    bool loopAudioMonitorEnable = false;
    List<uint> removeKeys = new List<uint>();

    class AudioSourceInfo {
        public AudioSource audioS;
        public float lastPlayTime;
        public AudioSourceInfo(AudioSource aAudioS, float time) {
            audioS = aAudioS;
            lastPlayTime = time;
        }
    }

    /// <summary>
    /// 回收已播放结束的AudioSource。
    /// </summary>
    /// <param name="audioS"></param>
    void RegistAudioGC(AudioSource audioS) {
        playingAudioClips.AddLast(audioS);
    }

    /// <summary>
    /// 设置循环音效的播放时间
    /// </summary>
    /// <param name="audioS"></param>
    /// <param name="playTime"></param>
    void RegistAudioLoopUpdate(uint audioKey, AudioSource audioS, float playTime) {
        if(playTime > 0) {
            //mark:youhua AudioSourceInfo PoolCache
            loopAudioTimeMonitor.Add(audioKey, new AudioSourceInfo(audioS, playTime));
            if(loopAudioMonitorEnable == false) {
                StartCoroutine(C_LoopAudioPlayTimeUpdate());
            }
        }
    }

    IEnumerator C_LoopAudioPlayTimeUpdate() {
        while(loopAudioTimeMonitor.Count > 0) {
            loopAudioMonitorEnable = true;
            float dt = Time.deltaTime;
            for(var ite = loopAudioTimeMonitor.GetEnumerator(); ite.MoveNext();) {
                AudioSourceInfo audioSInfo = ite.Current.Value;
                if(audioSInfo == null || audioSInfo.audioS == null || (audioSInfo.lastPlayTime -= dt) <= 0) {
                    if(audioSInfo.audioS != null) {
                        DeactiveAudioSource(audioSInfo.audioS);
                    }
                    removeKeys.Add(ite.Current.Key);
                }
            }
            for(int i = 0; i < removeKeys.Count; i++) {
                loopAudioTimeMonitor.Remove(removeKeys[i]);
            }
            yield return null;
        }
        loopAudioMonitorEnable = false;
    }

    public IEnumerator C_TimeToClearStoppedAudio() {
        while(true) {
            LinkedListNode<AudioSource> tmp = null;
            for(LinkedListNode<AudioSource> node = playingAudioClips.First; node != null;) {
                AudioSource audioS = node.Value;
                if(audioS == null || audioS.isPlaying == false) {
                    if(audioS) {
                        DeactiveAudioSource(audioS);
                    }
                    tmp = node.Next;
                    playingAudioClips.Remove(node);
                    node = tmp;
                } else {
                    node = node.Next;
                }
            }
            yield return new WaitForSeconds(TimeToClear_interval);
        }
    }

    void DeactiveAudioSource(AudioSource audioS) {
        if(audioS != null) {
            audioS.transform.parent = EffectsPool;
            audioS.Stop();
            //
            //audioS.gameObject.SetActive(false);
            //audioS.transform.parent = EffectsPool;
            audioS.clip = null;
            pausingSources.Remove(audioS);
            if(AudioSourcePool.BackToCache(audioS) == false) {
                GameObject.Destroy(audioS.gameObject);
            }
        }
    }

    IEnumerator DeactivateSourceObjAfterTime(AudioSource audioS, float time) {
        yield return new WaitForSeconds(time);

        DeactiveAudioSource(audioS);
    }

    AudioSource CreateAudioSourceForPool(bool isStoredObj) {
        GameObject go = new GameObject();
#if UNITY_EDITOR
        go.name = "Audio-freeAudioS";
#endif
        go.transform.parent = EffectsPool;
        AudioSource free = go.AddComponent<AudioSource>();
        free.dopplerLevel = 0;
        EffectsPool.name = "Effects Pool = " + (AudioSourcePool.getPoolCount() + 1);
        //if(isStoredObj) go.SetActive(false);
        return free;
    }
    #endregion Private Methods

    public void InitOnEnterScene() {
        //if(SceneMgr.Share().IsBattleScene()) {
        TimeToClear_interval = TimeToClear_FastTime;
        //} else {
        //    TimeToClear_interval = TimeToClear_SlowTime;
        //}
    }

    public void ClearOnExitScene() {
        userAudioSources.Clear();
        queuedClips.Clear();
        EffectsPool.DetachChildren();
        pausingSources.Clear();
        playingAudioClips.Clear();
        EffectsPool.name = "Effects Pool = 0" + AudioSourcePool.getPoolCount();
        AudioSourcePool.ClearPool();
        m_loadingAuido.Clear();
        m_controlAudio.Clear();
        fadeToClip = null;
    }
}

public struct AudioInfo {
    #region Fields

    public Vector3 position;
    public Transform parent;
    public string name;
    public float delay;
    public float minDistance;
    public float pitch;
    public float volume;

    #endregion Fields

    #region Constructors

    public AudioInfo(string aName, Vector3 aPos, float aDelay, Transform aParent = null, float aVolume = 1, float aPitch = 1, float aMinDistance = 10) {
        name = aName;
        position = aPos;
        parent = aParent;
        volume = aVolume;
        pitch = aPitch;
        minDistance = aMinDistance;
        delay = aDelay;
    }

    #endregion Constructors
}
}